/**
**************************************************************************************
*Founder Effect                                                                      *
*Copyright (C) 2006-2007, 5d Studios (www.5d-Studios.com)                            *
*                                                                                    *
*This library is free software; you can redistribute it and/or                       *
*modify it under the terms of the GNU Lesser General Public                          *
*License as published by the Free Software Foundation; either                        *
*version 2.1 of the License, or (at your option) any later version.                  *
*                                                                                    *
*This library is distributed in the hope that it will be useful,                     *
*but WITHOUT ANY WARRANTY; without even the implied warranty of                      *
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU                   *
*Lesser General Public License for more details.                                     *
*                                                                                    *
*You should have received a copy of the GNU Lesser General Public                    *
*License along with this library; if not, write to the Free Software                 *
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA      *
**************************************************************************************
*/
#include "StdAfx.h"
#include <stdarg.h>
#include "SDL.h"
#include "Console.h"

#include "input/Binder.h"
#include "shared/StringUtil.h"

#include "Kernel.h"

extern Kernel* g_kernel;
/**
=============================
Constructor -
=============================
*/
Console::Console(void)
{
    m_cons.m_display    = 150;

    m_cons.m_fraction   = 0.0f;
    m_cons.m_moving     = false;
    m_open              = false;

    m_currentCmd = 0;
    cmdStack.push_back("");

    SetName("Console");

}

/**
=============================
Init the console - add functions
=============================
*/
int Console::Init()
{
    // Register console functions
    m_commands.AddCommand( &exit_f, "quit" );
    m_commands.AddCommand( &clear_f, "clear" );
    m_commands.AddCommand( &exec_f , "exec" );
    m_commands.AddCommand( &set_f, "set" );
    m_commands.AddCommand( &create_f, "dcvar" );
    m_commands.AddCommand( &echo_f, "echo" );
    m_commands.AddCommand( &list_f, "cvarlist" );
    m_commands.AddCommand( &log_f, "logger" );
    m_commands.AddCommand( &run_f, "run" );

    //----END TEMP

    if ( m_logger.Init("client.log") ) {
        Print("Error Initializing logger!");
    }
    SetLog( true );
    // we initialized the console
    m_inited = true;

    Print("Console initialized...");
    return 0;
}


/**
=====================
Start the game state
=====================
*/
int Console::Start()
{
	// activate the buffer
	SetActive( true );
    m_buffer.SetActive( true );
	return 0;
}


/**
=====================
Exit out of the game state
=====================
*/
void Console::Exit()
{
	SetActive( false );
    m_buffer.SetActive( false );
    g_kernel->GetInput()->DeactivateBuffer( BUFFER_CONSOLE );
}


/**
=============================
Update the console
=============================
*/
void Console::Update(float dt)
{
    // Check to see if we should activate the console
    if ( !IsShowing() )
        return;

    /** 
     * Execute the given query
     */
    if ( g_kernel->GetInput()->IsKeyDown(SDLK_RETURN) ) {
        // Execute if possible
        Exec( m_buffer.GetBuffer() );

        // clear the buffers
        m_buffer.Clear();
    }

    /**
     * Page up or down
     */
    if ( g_kernel->GetInput()->IsKeyDown(SDLK_PAGEDOWN, true) ) {
        m_cons.m_display -=  2;
    }

    if ( g_kernel->GetInput()->IsKeyDown(SDLK_PAGEUP, true ) ) {
        m_cons.m_display +=  2;
    }

    /**
     * Scroll through old commands
     */
    if ( g_kernel->GetInput()->IsKeyDown(SDLK_DOWN) ) {
        if ( ++m_currentCmd > cmdStack.size()-1 )
            m_currentCmd = 0;
        m_buffer.SetBuffer( cmdStack[m_currentCmd] );
    }

    if ( g_kernel->GetInput()->IsKeyDown(SDLK_UP) ) {
        if ( --m_currentCmd < 0 )
            m_currentCmd = cmdStack.size()-1;
        m_buffer.SetBuffer( cmdStack[m_currentCmd] );
    }

    /**
     * Autocomplete a command
     */
    if ( g_kernel->GetInput()->IsKeyDown(SDLK_TAB) ) {
        AutoComplete( m_buffer.GetBuffer() );
    }

}


/**
=============================
Shutdown
=============================
*/
void Console::Shutdown()
{
	SetActive( false );
    m_buffer.SetActive( false );
}


/**
===============================
Gather input for the console
===============================
*/
void Console::GetInput()
{
    // deprecated
}

/**
==============================
Get the input buffer that belongs
to the console
==============================
*/
InputBuffer* Console::GetBuffer()
{
    return &m_buffer;
}

/**
=================================
Print to the console window
=================================
*/
void Console::Print(const char *s,...) 
{
    char buffer[1024];

	va_list arguments;
    // parse out the arguments, and put them in 
    // string format
    va_start(arguments, s );
        vsprintf_s(buffer, /*sizeof(buffer),*/ s, arguments);
	va_end(arguments);

    m_cons.m_printStack.push_back( buffer );
    if ( IsLogging() ) {    
        m_logger.Write(-1, buffer);
    }
}


/**
=============================
Try to find a command that fits
the current string
=============================
*/
void Console::AutoComplete( std::string &s )
{
    // if null return
    if ( s == "" )
        return;

    // else lets see if we can find any
    // matchs
    std::vector< std::string > list;
    type_Commands cmds = m_commands.GetAllCommands();
    type_Commands::iterator it;

    // search each command, if the given string is
    // equal to the first part of the command, add
    // it to the list
    for( it = cmds.begin(); it != cmds.end(); ++it )
    {
        if ( s.length() > it->first.length() )
            continue;

        int i = 0;
        for (; i < s.length(); i++ )
        {
            // not equal
            if (s[i] != it->first[i])
            {
                break;
            }
        }
        // we found a partial match
        if ( i == s.length() )
        {
            list.push_back( it->first );
        }

    }

    //
    // Now test to see if we have matchs
    
    // no matchs
    if ( !list.size() )
        return;
    // We have one match
    else if ( list.size() == 1 )
        m_buffer.SetBuffer( list[0] );
    // We have a couple of matchs
    else {
        bool b = true;
            
        int bufindex = s.length();
        /*
         * Append chars until we don't have a match
         *
         */
        while ( b ) {
            // search through each possible command/cvar
            for ( int i = 1; i < list.size(); i++ ) {
                // check to see if the possible is long enough
                if (list[0].length() <= bufindex || 
                    list[i].length() <= bufindex ) 
                {
                    b = false;
                    break;
                }
                else if(list[i].at(bufindex) != list[0].at(bufindex) ) {
                    b = false;
                    break;
                }
            }
            if ( b ) {
                // append the char
                m_buffer.SetBuffer( s.append( list[0].substr(bufindex++, 1) ) );
            }
        }
        // Print the results
        Print("---");
        for ( int j = 0; j < list.size(); j++ )
        {
            Print( list.at(j).c_str() );
        }
        Print("---");

    }
}

/**
=============================
Execute a command if possible
=============================
*/
void Console::Exec ( const std::string &s )
{   // Remember the cmd
    cmdStack.push_back ( s );
    
    // Do not let the cmd stack get to big
    if( cmdStack.size() > CMD_STACK )
    {
        cmdStack.pop_front();
    }
    StringUtil util(s);
    // fetch the cmd from the string
    std::string cmd = "";
    if ( util.HasNext() )
    {
        cmd = util.GetNext();
    } 
    std::string rest = "";
    while( util.HasNext() )
        rest += util.GetNext() + " ";
 
    // Execute the command
    ICommand *command = m_commands.GetCommand( cmd );

    if ( command )
        command->Exec( rest );
    else if ( !g_kernel->GetCvars()->IsValid( cmd ) )
        Print( s.c_str() );
}

/**
=============================
Toggle the console window
=============================
*/
void Console::Toggle()
{   // We always want the console to be
    // active
    m_open = !m_open;
    m_buffer.SetActive( !m_buffer.IsActive() );
       
    
    // Activate the console buffer
    if ( m_open ) {
		g_kernel->GetInput()->ActivateBuffer( BUFFER_CONSOLE );
    } else {
        // clear the buffer from last input
        m_buffer.SetBuffer( m_buffer.GetBuffer().substr(0, m_buffer.Length()-1 ) );
    }
    
    // we are either closing or opening the console
    m_cons.m_moving = true;
}


/** Add a command */
void Console::AddCommand( ICommand* cmd,  const std::string &s)
{
	m_commands.AddCommand( cmd, s );
}

/** Remove a command */
void Console::RemoveCommand( const std::string &s)
{
	m_commands.RemoveCommand( s );
}

/**
=============================
Deconstructor
=============================
*/
Console::~Console(void)
{

}

/*------------------------------------------------------------
    CmdList Command
------------------------------------------------------------*/

void Cmds::CmdList_f::Exec(std::string &s) 
{
    type_Commands list = m_cmd->GetAllCommands();
    type_Commands::iterator it = list.begin();
    for (; it != list.end(); it++ )
    {
        g_kernel->LPrintf( ("-- " + it->first).c_str() );
    }
    g_kernel->LPrintf( "    %d Total", list.size() );
}
